/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "NodeCpp/Data/Rel/SqlPreparedStatement.h"
#include "NodeCpp/String.h"
#include "NodeCpp/Data/Rel/Session.h"
#include "NodeCpp/Data/Rel/SqlConnection.h"

namespace NodeCpp {
namespace Data {
namespace Rel {

    SqlStmtParameters::SqlStmtParameters(std::uint32_t _ParamsNum)
    {
        if (_ParamsNum > 0) {
            Params_.reserve(_ParamsNum);
        }
    }

    void SqlStmtParameters::reset(const SqlStatement& _Stmt)
    {
        Params_.clear();
        if (_Stmt.GetArgumentsCount() > 0) {
            Params_.reserve(_Stmt.GetArgumentsCount());
        }
    }

    SqlStatement& SqlStatement::operator=(const SqlStatement& _Index)
    {
        if (this != &_Index)
        {
            Index_ = _Index.Index_;
            Session_ = _Index.Session_;

            Params_.reset();

            if (_Index.Params_.get()) {
                Params_.reset(new SqlStmtParameters(*(_Index.Params_)));
            }
        }

        return *this;
    }

    bool SqlStatement::Execute(void)
    {
        SqlStmtParameters* _Args = _Detach();
        if (_Args->BoundParams() != GetArgumentsCount())
        {
            Session_->_GetErrorHandler()(-1, "SQL ERROR: wrong amount of parameters (%i instead of %i)", _Args->BoundParams(), GetArgumentsCount());
            Session_->_GetErrorHandler()(-1, "SQL ERROR: statement: %s", Session_->GetStatementString(id()).c_str());
            NODECPP_ASSERT(false);
            return false;
        }

        return Session_->ExecuteStatement(Index_, _Args);
    }

    bool SqlStatement::DirectExecute(void)
    {
        SqlStmtParameters* _Args = _Detach();
        if (_Args->BoundParams() != GetArgumentsCount())
        {
            Session_->_GetErrorHandler()(-1, "SQL ERROR: wrong amount of parameters (%i instead of %i)", _Args->BoundParams(), GetArgumentsCount());
            Session_->_GetErrorHandler()(-1, "SQL ERROR: statement: %s", Session_->GetStatementString(id()).c_str());
            NODECPP_ASSERT(false);
            return false;
        }

        return Session_->DirectExecuteStatement(Index_, _Args);
    }

    SqlPlainPreparedStatement::SqlPlainPreparedStatement(const std::string& _Fmt, SqlConnection& _Conn)
        : SqlPreparedStatement(_Fmt, _Conn)
    {
		Prepared_ = true;
        Params_ = (std::uint32_t)std::count(Format_.begin(), Format_.end(), '?');
        Query_ = NodeCpp::strcasecmp(Format_.c_str(), "select") == 0;
    }

    void SqlPlainPreparedStatement::Bind(const SqlStmtParameters& _Holder)
    {
        if (Params_ != _Holder.BoundParams())
        {
            NODECPP_ASSERT(false);
            return;
        }

        // reset resulting plain SQL request
        PlainRequest_ = Format_;
        size_t _LastPos = 0;

        SqlStmtParameters::ParameterContainer const& _Args = _Holder.params();

        SqlStmtParameters::ParameterContainer::const_iterator _IterLast = _Args.end();
        for (SqlStmtParameters::ParameterContainer::const_iterator _Iter = _Args.begin();
            _Iter != _IterLast; ++_Iter)
        {
            const SqlStmtFieldData& _Data = (*_Iter);

            std::ostringstream _Fmt;
            dataToString(_Data, _Fmt);

            _LastPos = PlainRequest_.find('?', _LastPos);
            if (_LastPos != std::string::npos)
            {
                std::string _Tmp = _Fmt.str();
                PlainRequest_.replace(_LastPos, 1, _Tmp);
                _LastPos += _Tmp.length();
            }
        }
    }

    bool SqlPlainPreparedStatement::Execute(void)
    {
        if (PlainRequest_.empty()) {
            return false;
        }

        return Conn_.Execute(PlainRequest_.c_str());
    }

    void SqlPlainPreparedStatement::dataToString(const SqlStmtFieldData& _Data, std::ostringstream& _Fmt)
    {
        switch (_Data.type())
        {
        case FIELD_BOOL:    _Fmt << "'" << std::uint32_t(_Data.AsBool()) << "'";     break;
        case FIELD_UI8:     _Fmt << "'" << std::uint32_t(_Data.AsUint8()) << "'";    break;
        case FIELD_UI16:    _Fmt << "'" << std::uint32_t(_Data.AsUint16()) << "'";   break;
        case FIELD_UI32:    _Fmt << "'" << _Data.AsUint32() << "'";           break;
        case FIELD_UI64:    _Fmt << "'" << _Data.AsUint64() << "'";           break;
        case FIELD_I8:      _Fmt << "'" << std::int32_t(_Data.AsInt8()) << "'";      break;
        case FIELD_I16:     _Fmt << "'" << std::int32_t(_Data.AsInt16()) << "'";     break;
        case FIELD_I32:     _Fmt << "'" << _Data.AsInt32() << "'";            break;
        case FIELD_I64:     _Fmt << "'" << _Data.AsInt64() << "'";            break;
        case FIELD_FLOAT:   _Fmt << "'" << _Data.AsFloat() << "'";            break;
        case FIELD_DOUBLE:  _Fmt << "'" << _Data.AsDouble() << "'";           break;
        case FIELD_STRING:
        {
                             std::string _Tmp = _Data.AsStr();
                             Conn_.session().EscapeString(_Tmp);
                             _Fmt << "'" << _Tmp << "'";
                             break;
        }
        case FIELD_NONE:                                                    break;
        }
    }

}}}
